#pragma once

#include <memory>
#include <string>
#include <type_traits>


class HttpResponseBuffer
{
public:
    virtual ~HttpResponseBuffer() {}

    virtual const char *data() const = 0;

    virtual size_t size() const = 0;
};

class HttpResponseBufferHolder
{
public:
    HttpResponseBufferHolder() = default;

    HttpResponseBufferHolder(HttpResponseBufferHolder &&other) noexcept
        : ptr_(std::move(other.ptr_)) {}

    HttpResponseBufferHolder &operator=(HttpResponseBufferHolder &&other) noexcept {
        ptr_ = std::move(other.ptr_);
        return *this;
    }

    template <typename BufferType>
    BufferType &as() {
        static_assert(std::is_base_of_v<HttpResponseBuffer, BufferType>);
        if (ptr_) {
            if (BufferType *p = dynamic_cast<BufferType *>(ptr_.get())) {
                return *p;
            } else {
                BufferType *newBuffer = new BufferType;
                ptr_ = std::unique_ptr<BufferType>(newBuffer);
                return *newBuffer;
            }
        } else {
            BufferType *newBuffer = new BufferType;
            ptr_ = std::unique_ptr<BufferType>(newBuffer);
            return *newBuffer;
        }
    }

    const char *data() const {
        if (ptr_) {
            return ptr_->data();
        } else {
            return nullptr;
        }
    }

    size_t size() const {
        if (ptr_) {
            return ptr_->size();
        } else {
            return 0;
        }
    }

private:
    std::unique_ptr<HttpResponseBuffer> ptr_ = nullptr;
};

class OwnMemoryHttpResponseBuffer : public HttpResponseBuffer
{
public:
    virtual ~OwnMemoryHttpResponseBuffer() {}

    virtual const char *data() const {
        return buffer_.data();
    }

    virtual size_t size() const {
        return buffer_.size();
    }

    std::string &buffer() {
        return buffer_;
    }

private:
    std::string buffer_;
};
